package com.uuah.server.transformer;

import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.xml.transform.stream.StreamSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uuah.exception.lifecycle.InitialisationException;
import com.uuah.server.api.IUuahMessage;
import com.uuah.server.api.endpoint.IUuahEndpoint;
import com.uuah.server.api.transformer.IBaseTransformer;
import com.uuah.server.api.transport.IMessageAdapter;
import com.uuah.server.config.i18n.ServerMessages;
import com.uuah.server.exception.transformer.TransformerException;
import com.uuah.server.transport.NullPayload;
import com.uuah.utils.ClassUtils;
import com.uuah.utils.FileUtils;
import com.uuah.utils.StringUtils;

/**
 *
 * TODO comment ...
 *
 * <p>
 * update record:updator，update time，update content and version
 * </p>
 *
 * @author <a href="jonny_quan@hotmail.com">jonny</a>
 * @date 2009 2009-3-27 下午02:06:40
 * @version 1.0.0
 */
public abstract class AbstractTransformer implements IBaseTransformer {
	protected static final int DEFAULT_TRUNCATE_LENGTH = 200;

	protected transient final Logger logger = LoggerFactory
			.getLogger(getClass());

	protected Class returnClass = Object.class;

	protected String name = null;

	protected IUuahEndpoint endpoint = null;

	protected final List sourceTypes = new CopyOnWriteArrayList();

	public AbstractTransformer() {
		super();
	}

	protected Object checkReturnClass(Object object)
			throws TransformerException {
		if (returnClass != null) {
			if (!returnClass.isInstance(object)) {
				throw new TransformerException(
						ServerMessages.transformUnexpectedType(object
								.getClass(), returnClass), this);
			}
		}

		if (logger.isDebugEnabled()) {
			logger
					.debug("The transformed object is of expected type. Type is: "
							+ ClassUtils.getSimpleName(object.getClass()));
		}

		return object;
	}

	protected void registerSourceType(Class aClass) {
		if (!sourceTypes.contains(aClass)) {
			sourceTypes.add(aClass);

			if (aClass.equals(Object.class)) {
				if (logger.isDebugEnabled()) {
					logger
							.debug("java.lang.Object has been added as source type for this transformer, there will be no source type checking performed");
				}
			}
		}
	}

	protected void unregisterSourceType(Class aClass) {
		sourceTypes.remove(aClass);
	}

	public String getName() {
		if (name == null) {
			name = this.generateTransformerName();
		}
		return name;
	}

	public void setName(String string) {
		if (string == null) {
			string = ClassUtils.getSimpleName(this.getClass());
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Setting transformer name to: " + string);
		}
		name = string;
	}

	public Class getReturnClass() {
		return returnClass;
	}

	public void setReturnClass(Class newClass) {
		returnClass = newClass;
	}

	public boolean isSourceTypeSupported(Class aClass) {
		return isSourceTypeSupported(aClass, false);
	}

	public boolean isSourceTypeSupported(Class aClass, boolean exactMatch) {
		int numTypes = sourceTypes.size();

		if (numTypes == 0) {
			return !exactMatch;
		}

		for (int i = 0; i < numTypes; i++) {
			Class anotherClass = (Class) sourceTypes.get(i);
			if (exactMatch) {
				if (anotherClass.equals(aClass)) {
					return true;
				}
			} else if (anotherClass.isAssignableFrom(aClass)) {
				return true;
			}
		}

		return false;
	}

	public final Object transform(Object src) throws TransformerException {
		String encoding = null;

		Object payload = src;
		IMessageAdapter adapter;
		if (src instanceof IMessageAdapter) {
			encoding = ((IMessageAdapter) src).getEncoding();
			adapter = (IMessageAdapter) src;
			if ((!isSourceTypeSupported(IMessageAdapter.class, true)
					&& !isSourceTypeSupported(IUuahMessage.class, true) && !(this instanceof AbstractMessageAwareTransformer))) {
				src = ((IMessageAdapter) src).getPayload();
				payload = adapter.getPayload();
			}
		}

		if (encoding == null && endpoint != null) {
			encoding = endpoint.getEncoding();
		} else if (encoding == null) {
			encoding = FileUtils.DEFAULT_ENCODING;
		}

		Class srcCls = src.getClass();
		if (!isSourceTypeSupported(srcCls)) {
			throw new TransformerException(ServerMessages
					.transformOnObjectUnsupportedTypeOfEndpoint(this.getName(),
							payload.getClass(), endpoint), this);
		}

		if (logger.isDebugEnabled()) {
			logger.debug("Applying transformer " + getName() + " ("
					+ getClass().getName() + ")");
			logger.debug("Object before transform: "
					+ StringUtils.truncate(StringUtils.toString(payload),
							DEFAULT_TRUNCATE_LENGTH, false));
		}

		Object result;
		result = doTransform(payload, encoding);

		if (result == null) {
			result = NullPayload.getInstance();
		}

		if (logger.isDebugEnabled()) {
			logger.debug("Object after transform: "
					+ StringUtils.truncate(StringUtils.toString(result),
							DEFAULT_TRUNCATE_LENGTH, false));
		}

		result = checkReturnClass(result);
		return result;
	}

	protected boolean isConsumed(Class srcCls) {
		return InputStream.class.isAssignableFrom(srcCls)
				|| StreamSource.class.isAssignableFrom(srcCls);
	}

	public IUuahEndpoint getEndpoint() {
		return endpoint;
	}

	public void setEndpoint(IUuahEndpoint endpoint) {
		this.endpoint = endpoint;
	}

	protected abstract Object doTransform(Object src, String encoding)
			throws TransformerException;

	public void initialise() throws InitialisationException {
		// do nothing, subclasses may override
	}

	protected String generateTransformerName() {
		String name = ClassUtils.getSimpleName(this.getClass());
		int i = name.indexOf("To");
		if (i > 0 && returnClass != null) {
			String target = ClassUtils.getSimpleName(returnClass);
			if (target.equals("byte[]")) {
				target = "byteArray";
			}
			name = name.substring(0, i + 2) + StringUtils.capitalize(target);
		}
		return name;
	}

	public List getSourceTypes() {
		return Collections.unmodifiableList(sourceTypes);
	}

	// @Override
	public String toString() {
		StringBuffer sb = new StringBuffer(80);
		sb.append(ClassUtils.getSimpleName(this.getClass()));
		sb.append("{this=").append(
				Integer.toHexString(System.identityHashCode(this)));
		sb.append(", name='").append(name).append('\'');
		sb.append(", returnClass=").append(returnClass);
		sb.append(", sourceTypes=").append(sourceTypes);
		sb.append('}');
		return sb.toString();
	}

	public boolean isAcceptNull() {
		return false;
	}

}
